﻿using CommonUtility;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using CSRedis;

namespace WPay.Infrastructure.Unity
{
    public class Redis
    {
        private int DbNum { get; }
        private static readonly object Locker = new object();

        public string CustomKey;


        int Default_Timeout = 15;//默认超时时间（单位秒）

        #region 构造函数

        /// <summary>
        /// dbNum设置连接数据库
        /// </summary>
        /// <param name="dbNum"></param>
        public Redis(int dbNum = 13)
            : this(dbNum, null)
        {

        }
        public Redis(int dbNum, string readWriteHosts)
        {

            DbNum = dbNum;
            try
            {
                var Instance = RedisHelper.Instance;
                if (Instance == null)
                {
                    lock (Locker)
                    {
                        LoggerHelper.Log("RedisHelper初始化失败");
                        RedisHelper.Initialization(new CSRedis.CSRedisClient(ConfigManage.GetConfigByKey("RedisConn")));
                      
                    }
                }
            }
            catch (Exception e)
            {
                LoggerHelper.Log("RedisHelper重新初始化");
                RedisHelper.Initialization(new CSRedisClient(ConfigManage.GetConfigByKey("RedisConn")));

            }
        }

        #endregion 构造函数

        #region String

        #region 同步方法

        /// <summary>
        /// 保存单个key value
        /// </summary>
        /// <param name="key">Redis Key</param>
        /// <param name="value">保存的值</param>
        /// <param name="expiry">过期时间</param>
        /// <returns></returns>
        public bool StringSet(string key, string value, TimeSpan? expiry = default(TimeSpan?))
        {

            ///return Do(db => db.StringSet(key, value, expiry));
            if (expiry==null)
            {
                return RedisHelper.Set(key, value);
            }
            else
            {
                return RedisHelper.Set(key, value, expiry.Value);
            }
        }
        /// <summary>
        /// 保存一个对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public bool StringSet<T>(string key, T obj, TimeSpan? expiry = default(TimeSpan?))
        {
          
            string json = ConvertJson(obj);
            if (expiry == null)
            {
                return RedisHelper.Set(key, json);
            }
            else
            {
                return RedisHelper.Set(key, json, expiry.Value);
            }
          
        }

        /// <summary>
        /// 获取单个key的值
        /// </summary>
        /// <param name="key">Redis Key</param>
        /// <returns></returns>
        public string StringGet(string key)
        {
        
            //return Do(db => db.StringGet(key));
            return RedisHelper.Get(key);
        }

        /// <summary>
        /// 获取一个key的对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public T StringGet<T>(string key)
        {
           
            // return Do(db => ConvertObj<T>(db.StringGet(key)));
            return RedisHelper.Get<T>(key);
        }
        #endregion 同步方法

        #region 异步方法



        /// <summary>
        /// 保存一个对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public async Task<bool> StringSetAsync<T>(string key, T obj, TimeSpan? expiry = default(TimeSpan?))
        {

            string json = ConvertJson(obj);
            return await RedisHelper.SetAsync(key, json, expiry.Value);
        }

        /// <summary>
        /// 获取单个key的值
        /// </summary>
        /// <param name="key">Redis Key</param>
        /// <returns></returns>
        public async Task<string> StringGetAsync(string key)
        {

            return await RedisHelper.GetAsync(key);
        }



        ///// <summary>
        ///// 获取多个Key
        ///// </summary>
        ///// <param name="listKey">Redis Key集合</param>
        ///// <returns></returns>
        //public async Task<string[]> StringGetAsync(List<string> listKey)
        //{
        //    List<string> newKeys = listKey.Select(AddSysCustomKey).ToList();
        //    return await RedisHelper.MGetAsync<string[]>(listKey);
        //}



        /// <summary>
        /// 获取一个key的对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<T> StringGetAsync<T>(string key)
        {
            return await RedisHelper.GetAsync<T>(key);
        }

      
      

        #endregion 异步方法

        #endregion String


        public void SetAdd<T>(string key, params T[] value)
        {
        
            RedisHelper.SAdd(key, value);
        }
        /// <summary>
        /// 差集
        /// </summary>
        /// <param name="firstkey"></param>
        /// <param name="secondkey"></param>
        /// <returns></returns>
        public string[] Difference(string firstkey, string secondkey)
        {

            string[] dif = RedisHelper.SDiff( firstkey, secondkey);
            return dif;
        }
        /// <summary>
        /// 并集
        /// </summary>
        /// <param name="firstkey"></param>
        /// <param name="secondkey"></param>
        /// <returns></returns>
        public string[] Union(string firstkey, string secondkey)
        {

            string[] union = RedisHelper.SUnion(firstkey, secondkey);
            return union;
        }
        /// <summary>
        /// 交集
        /// </summary>
        /// <param name="firstkey"></param>
        /// <param name="secondkey"></param>
        /// <returns></returns>
        public string[] Intersect(string firstkey, string secondkey)
        {
            string[] union = RedisHelper.SInter(firstkey, secondkey);
            return union;
        }

        #region Hash

        #region 同步方法

        /// <summary>
        /// 判断某个数据是否已经被缓存
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <returns></returns>
        public bool HashExists(string key, string dataKey)
        {
          
            return RedisHelper.HExists(key, dataKey);
        }

       

       
        

        /// <summary>
        /// 为数字增长val
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <param name="val">可以为负</param>
        /// <returns>增长后的值</returns>
        public double HashIncrement(string key, string dataKey, double val = 1)
        {
          
            //return Do(db => db.HashIncrement(key, dataKey, val));
            return RedisHelper.HIncrBy(key, dataKey);
        }

        /// <summary>
        /// 为数字减少val
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <param name="val">可以为负</param>
        /// <returns>减少后的值</returns>
        public double HashDecrement(string key, string dataKey, double val = 1)
        {
          
            //  return Do(db => db.HashDecrement(key, dataKey, val));
            return RedisHelper.HDel(key, dataKey);
        }

      
        

        #endregion 同步方法

        #endregion Hash

        #region List

        #region 同步方法

        /// <summary>
        /// 移除指定ListId的内部List的值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void ListRemove<T>(string key, T value)
        {
            RedisHelper.LRem(key, 1, value);
        }

        /// <summary>
        /// 获取指定key的List
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public List<T> ListRange<T>(string key)
        {
            try
            {
                //key = AddSysCustomKey(key);
                //return Do(redis =>
                //{
                //    var values = RedisHelper.LRange(key, 0, -1);
                //    return ConvetList<T>(values);
                //});

               return  RedisHelper.LRange<T>(key, 0, -1).ToList();
            }
            catch (Exception e)
            {
                LoggerHelper.Log($"ListRange异常：{e.Message + e.InnerException?.Message}");
                return new System.Collections.Generic.List<T>();
            }
        }

        /// <summary>
        /// 入队
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void ListRightPush<T>(string key, T value, TimeSpan? expiry = default(TimeSpan?))
        {
         
            // Do(db => db.KeyExpire(key, expiry));
            // Do(db => db.ListRightPush(key, ConvertJson(value)));
            RedisHelper.LPush(key, value);

        }
        /// <summary>
        /// 获取集合中满足条件的某个数据
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public T ListByOne<T>(string key, Expression<Func<T, bool>> where)
        {
            var list = ListRange<T>(key);
            return list.AsQueryable().FirstOrDefault(where);
        }
        /// <summary>
        /// 获取集合中满足条件的某个数据
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public List<T> List<T>(string key, Expression<Func<T, bool>> where)
        {
            var list = ListRange<T>(key);
            return list.AsQueryable().Where(where).ToList();
        }

        #endregion 同步方法

        #region 异步方法

        ///// <summary>
        ///// 移除指定ListId的内部List的值
        ///// </summary>
        ///// <param name="key"></param>
        ///// <param name="value"></param>
        //public async Task<long> ListRemoveAsync<T>(string key, T value)
        //{
        //    key = AddSysCustomKey(key);
        //    return await Do(db => db.ListRemoveAsync(key, ConvertJson(value)));
        //}

        ///// <summary>
        ///// 获取指定key的List
        ///// </summary>
        ///// <param name="key"></param>
        ///// <returns></returns>
        //public async Task<List<T>> ListRangeAsync<T>(string key)
        //{
        //    key = AddSysCustomKey(key);
        //    var values = await RedisHelper.LRangeAsync(key, 0, -1);
        //    return ConvetList<T>(values);
        //}

        ///// <summary>
        ///// 入队
        ///// </summary>
        ///// <param name="key"></param>
        ///// <param name="value"></param>
        //public async Task<long> ListRightPushAsync<T>(string key, T value)
        //{
        //    key = AddSysCustomKey(key);
        //    return await Do(db => db.ListRightPushAsync(key, ConvertJson(value)));
        //}

        ///// <summary>
        ///// 出队
        ///// </summary>
        ///// <typeparam name="T"></typeparam>
        ///// <param name="key"></param>
        ///// <returns></returns>
        //public async Task<T> ListRightPopAsync<T>(string key)
        //{
        //    key = AddSysCustomKey(key);
        //    var value = await Do(db => db.ListRightPopAsync(key));
        //    return ConvertObj<T>(value);
        //}

        ///// <summary>
        ///// 入栈
        ///// </summary>
        ///// <typeparam name="T"></typeparam>
        ///// <param name="key"></param>
        ///// <param name="value"></param>
        //public async Task<long> ListLeftPushAsync<T>(string key, T value)
        //{
        //    key = AddSysCustomKey(key);
        //    return await Do(db => db.ListLeftPushAsync(key, ConvertJson(value)));
        //}

        ///// <summary>
        ///// 出栈
        ///// </summary>
        ///// <typeparam name="T"></typeparam>
        ///// <param name="key"></param>
        ///// <returns></returns>
        //public async Task<T> ListLeftPopAsync<T>(string key)
        //{
        //    key = AddSysCustomKey(key);
        //    var value = await Do(db => db.ListLeftPopAsync(key));
        //    return ConvertObj<T>(value);
        //}

        ///// <summary>
        ///// 获取集合中的数量
        ///// </summary>
        ///// <param name="key"></param>
        ///// <returns></returns>
        //public async Task<long> ListLengthAsync(string key)
        //{
        //    key = AddSysCustomKey(key);
        //    return await Do(redis => redis.ListLengthAsync(key));
        //}
        #endregion 异步方法

        #endregion List

        #region SortedSet 有序集合

        #region 同步方法

        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="score"></param>
        public bool SortedSetAdd(string key, string value, double score)
        {
            try
            {
            
                //return Do(redis => redis.SortedSetAdd(key, ConvertJson<T>(value), score));
                RedisHelper.SortAndStore(key, value);
                return true;
            }
            catch (Exception e)
            {
                LoggerHelper.Log($"SortedSetAdd异常：{e.Message + e.InnerException?.Message}");
                return false;
            }
        }
      


        #endregion 同步方法



        #endregion SortedSet 有序集合

        #region key

        /// <summary>
        /// 删除单个key
        /// </summary>
        /// <param name="key">redis key</param>
        /// <returns>是否删除成功</returns>
        public bool KeyDelete(string key)
        {
            if (KeyExists(key))
            {
                return RedisHelper.Del(key) > 0;
            }
            else
            {
                return true;
            }
        }

        /// <summary>
        /// 删除指定条件key
        /// </summary>
        /// <param name="cpnid"></param>
        public void KeyDeletepattern(string pattern)
        {


            RedisHelper.Del(pattern);

            //var redisResult = RedisHelper.ScriptEvaluate(LuaScript.Prepare(
            //                //Redis的keys模糊查询：
            //                " local res = redis.call('KEYS', @keypattern) " +
            //                " return res "), new { @keypattern = pattern });
            //if (!redisResult.IsNull)
            //{
            //    db.KeyDelete(((RedisKey[])redisResult));  //删除一组key
            //}
        }



        /// <summary>
        /// 判断key是否存储
        /// </summary>
        /// <param name="key">redis key</param>
        /// <returns></returns>
        public bool KeyExists(string key)
        {

            return RedisHelper.Exists(key);
        }





        #endregion key

        #region 发布订阅

        /// <summary>
        /// Redis发布订阅  订阅
        /// </summary>
        /// <param name="subChannel"></param>
        /// <param name="handler"></param>
        public void Subscribe(string subChannel)
        {

            //普通订阅
            //RedisHelper.Subscribe(
            // ("chan1", msg => Console.WriteLine(msg.Body)),
            // ("chan2", msg => Console.WriteLine(msg.Body)));

            ////模式订阅（通配符）
            //RedisHelper.PSubscribe(new[] { "test*", "*test001", "test*002" }, msg => {
            //    Console.WriteLine($"PSUB  {msg.MessageId}:{msg.Body}  {msg.Pattern}: chan:{msg.Channel}");
            //});
            //模式订阅已经解决的难题：
            //1、分区的节点匹配规则，导致通配符最大可能匹配全部节点，所以全部节点都要订阅
            //2、本组 "test*", "*test001", "test*002" 订阅全部节点时，需要解决同一条消息不可执行多次

            //发布
            //RedisHelper.Publish("chan1", "123123123");
            //无论是分区或普通模式，rds.Publish 都可以正常通信


        }

        /// <summary>
        /// Redis发布订阅  发布
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="channel"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        public long Publish(string channel, string msg)
        {
           return  RedisHelper.Publish(channel, msg);
        }


     

        #endregion 发布订阅

        #region 其他

       

        /// <summary>
        /// 设置前缀
        /// </summary>
        /// <param name="customKey"></param>
        public void SetSysCustomKey(string customKey)
        {
            CustomKey = customKey;
        }

        #endregion 其他

        #region 辅助方法

      

     

        private string ConvertJson<T>(T value)
        {
            var Settings = new JsonSerializerSettings
            {
                //这句是解决问题的关键,也就是json.net官方给出的解决配置选项.                 
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
                DateFormatHandling = DateFormatHandling.IsoDateFormat,
                DateParseHandling = DateParseHandling.DateTime,
                DateFormatString = "yyyy-MM-dd HH:mm:ss"
            };
            string result = value is string ? value.ToString() : JsonConvert.SerializeObject(value, Settings);
            return result;
        }

        private T ConvertObj<T>(string value)
        {
            try
            {
                return JsonConvert.DeserializeObject<T>(value);
            }
            catch (Exception e)
            {
                return default(T);

            };
        }

        private List<T> ConvetList<T>(string[] values)
        {
            try
            {
                List<T> result = new List<T>();
                foreach (var item in values)
                {
                    var model = ConvertObj<T>(item);
                    result.Add(model);
                }
                return result;
            }
            catch (Exception e)
            {
                return default(List<T>);
            }
        }

        private string[] ConvertRedisKeys(List<string> redisKeys)
        {
            return redisKeys.Select(redisKey => (string)redisKey).ToArray();
        }

        #endregion 辅助方法
    }
}
